﻿/*=============================================================================
PostProcessRain : rain related post processing
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* 23/02/2005: Re-factored/Converted to CryEngine 2.0 by Tiago Sousa
* Created by Tiago Sousa

=============================================================================*/

#include "StdAfx.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"

#pragma warning(disable: 4244)

//#pragma optimize("", off)

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

const char *CRainDrops::GetName() const
{
	return "RainDrops";
}

bool CRainDrops::Preprocess()
{  
  bool bQualityCheck = CPostEffectsMgr::CheckPostProcessQuality( eRQ_Medium, eSQ_Medium );
  if( !bQualityCheck )
    return false;

  if(CRenderer::CV_r_RainDropsEffect == 0)
    return false;
  
  bool bRainActive = IsActiveRain();
  bool bMoistureActive = IsActiveMoisture();

  if(m_bFirstFrame)
  { //initialize with valid value on 1st frame
	m_pPrevView = PostProcessUtils().m_pView;
  }

  if(CRenderer::CV_r_RainDropsEffect > 2)
	  return true;
 
  return bRainActive || bMoistureActive;
}


bool CRainDrops::IsActiveRain()
{
	static float s_fLastSpawnTime = -1.0f;

	if( m_pAmount->GetParam() > 0.09f || m_nAliveDrops)
	{
		s_fLastSpawnTime = 0.0f;
		return true;
	}

	if( s_fLastSpawnTime == 0.0f)
	{
		s_fLastSpawnTime = PostProcessUtils().m_pTimer->GetCurrTime();
	}

	if( fabs(PostProcessUtils().m_pTimer->GetCurrTime() - s_fLastSpawnTime) < 1.0f ) 
	{
		return true;
	}
	return false;
}


bool CRainDrops::IsActiveMoisture()
{
	if( m_pMoistureAmount->GetParam() > 0.05)
	{
		m_fMoistureFade	= 1.0f;
	}
	else
	{
		m_fMoistureFade	= 0.0f;
	}

	if( m_pMoistureAmount->GetParam() > 0.05
	||	m_fMoistureCoverage > EPSILON	)
	{
		m_bMoisture = true;
		return true;
	}
	return false;		 
}


void CRainDrops::SpawnParticle( SRainDrop *&pParticle, int iRTWidth, int iRTHeight )
{
  static SRainDrop pNewDrop;

  static float fLastSpawnTime = 0.0f;

  float fUserSize = 5.0f;//m_pSize->GetParam();
  float fUserSizeVar = 2.5f;//m_pSizeVar->GetParam();

  if( Random() > 0.5f && fabs(PostProcessUtils().m_pTimer->GetCurrTime() - fLastSpawnTime) > m_pSpawnTimeDistance->GetParam() )
  {
    pParticle->m_pPos.x = Random();
    pParticle->m_pPos.y = Random();

    pParticle->m_fLifeTime = (pNewDrop.m_fLifeTime + pNewDrop.m_fLifeTimeVar *(Random()*2.0f-1.0f) ) ;
    //pParticle->m_fSize = (pNewDrop.m_fSize + pNewDrop.m_fSizeVar * (Random()*2.0f-1.0f)) * 2.5f;    
    //pNewDrop.m_fSize + pNewDrop.m_fSizeVar
    pParticle->m_fSize = 1.0f /( 10.0f *(fUserSize  + 0.5f * (fUserSizeVar) * (Random()*2.0f-1.0f)) ); 

	pParticle->m_pPos.x -= pParticle->m_fSize  / (float)iRTWidth;
	pParticle->m_pPos.y -= pParticle->m_fSize  / (float)iRTHeight;

    pParticle->m_fSpawnTime = PostProcessUtils().m_pTimer->GetCurrTime();  
    pParticle->m_fWeight = 0.0f; // default weight to force rain drop to be stopped for a while
    
    fLastSpawnTime = PostProcessUtils().m_pTimer->GetCurrTime();
  }
  else
  {
    pParticle->m_fSize = 0.0f;
  }

}

void CRainDrops::UpdateParticles(int iRTWidth, int iRTHeight)
{
  SRainDropsItor pItor, pItorEnd = m_pDropsLst.end();

  // Store camera parameters
  Vec3 vz = gcpRendD3D->GetRCamera().Z;    // front vec
  float fDot = vz.Dot(Vec3(0, 0, -1.0f));
  float fGravity = 1.0f - fabs( fDot );

  float fCurrFrameTime = 10.0f * gEnv->pTimer->GetFrameTime();

  bool bAllowSpawn( m_pAmount->GetParam() > 0.005f );

  m_nAliveDrops = 0;
  static int s_nPrevAliveDrops = 0;

  for(pItor=m_pDropsLst.begin(); pItor!=pItorEnd; ++pItor )
  {
    SRainDrop *pCurr = (*pItor);

    float fCurrLifeTime = (PostProcessUtils().m_pTimer->GetCurrTime() - pCurr->m_fSpawnTime) / pCurr->m_fLifeTime;

    //if( fDot < - 0.5 )
    {
      //pCurr->m_fLifeTime = -1.0f;
      //continue;
    }

    // particle died, spawn new 
    if( fabs(fCurrLifeTime) > 1.0f || pCurr->m_fSize < 0.01f)
    {
      if( bAllowSpawn )
      {
        SpawnParticle( pCurr, iRTWidth, iRTHeight );
      }
      else
      {
        pCurr->m_fSize = 0.0f; 
        continue;
      }
    }

    m_nAliveDrops++;	

    // update position, etc

    // add gravity
    pCurr->m_pPos.y += m_pVelocityProj.y * ((Random() *2.0f - 1.0f)*0.6f+0.4f); 
    pCurr->m_pPos.y += fCurrFrameTime * fGravity * min( pCurr->m_fWeight, 0.5f * pCurr->m_fSize);
    // random horizontal movement and size + camera horizontal velocity    
    pCurr->m_pPos.x += m_pVelocityProj.x * ((Random() *2.0f - 1.0f)*0.6f+0.4f); 
    pCurr->m_pPos.x += fCurrFrameTime * (min(pCurr->m_fWeight, 0.25f * pCurr->m_fSize) * fGravity * ( (Random() *2.0f - 1.0f)));  

    // Increase/decrease weight randomly 
    pCurr->m_fWeight = clamp_tpl<float>(pCurr->m_fWeight + fCurrFrameTime * pCurr->m_fWeightVar * (Random()*2.0f-1.0f)*4.0f, 0.0f, 1.0f );                    
  }

  if(	s_nPrevAliveDrops == 0 
	&&	m_nAliveDrops > 0)
  {	  
	m_bFirstFrame = true;
  }
//   else
//   {
// 	m_bFirstFrame = false;
//   }
  s_nPrevAliveDrops = m_nAliveDrops;  
}


static float Random2(float fVar)
{
	return (2.0f + Random()) * 0.5f * fVar;
}

void CRainDrops::UpdateMoisture()
{
	const float	fDeltaTime		= PostProcessUtils().m_pTimer->GetFrameTime();
	const float fMoistureVar	= m_pMoistureVariation->GetParam();
	const float fMoistureSpeed	= m_pMoistureSpeed->GetParam();
	const float	fMoistureDelta	= fMoistureSpeed * fDeltaTime;
	

	if(m_bFirstFrame)
	{
		//initialize moisture noise texture offsets
		m_vMoistureNoiseOffsets.x	= Random2(fMoistureVar);
		m_vMoistureNoiseOffsets.y	= Random2(fMoistureVar);
		m_vMoistureNoiseOffsets.z	= Random2(fMoistureVar);
		m_vMoistureNoiseOffsets.w	= Random2(fMoistureVar);

		//initialize moisture drops texture offsets		
		m_vMoistureDropsOffsets.x	= Random2(fMoistureVar);
		m_vMoistureDropsOffsets.y	= Random2(fMoistureVar);
		m_vMoistureDropsOffsets.z	= Random2(fMoistureVar);
		m_vMoistureDropsOffsets.w	= Random2(fMoistureVar);

		//randomize moisture random factors
		m_vMoistureRandom.x	= Random2(fMoistureVar);
		m_vMoistureRandom.y	= Random2(fMoistureVar);
		m_vMoistureRandom.z	= Random2(fMoistureVar);
		m_vMoistureRandom.w	= Random2(fMoistureVar);

		//initialize drop factors
		m_vMoistureDropsFactors = Vec4(0,0,0,0);

		m_fMoistureStrength = 0.0f;
		m_fMoistureCoverage = 0.0f;
		m_fMoistureFogCoverage = 0.0f;
	}

	

	//update moisture coverage	
	m_fMoistureCoverage		+= (m_pMoistureAmount->GetParam() - m_fMoistureCoverage) * fMoistureDelta;	// * (1 + Random2(fMoistureVar))

	//update fog coverage
	m_fMoistureFogCoverage	+= (m_pMoistureFogAmount->GetParam() - m_fMoistureFogCoverage) * fMoistureDelta;

	//update moisture strength	
	m_fMoistureStrength		+= (m_pMoistureDropletAmount->GetParam() - m_fMoistureStrength) * fMoistureDelta;	

	//update offsets
	const float fNoiseSpeed = fMoistureSpeed * fDeltaTime * 0.01f;
	m_vMoistureNoiseOffsets.x = 0;	//sin()
	m_vMoistureNoiseOffsets.y -= fNoiseSpeed;	//move down
	m_vMoistureNoiseOffsets.z = 0;	//sin()
	m_vMoistureNoiseOffsets.w -= 2 * fNoiseSpeed;	//move down

	const float fDropsSpeed = fMoistureSpeed * fDeltaTime * 0.005f;
	m_vMoistureDropsOffsets.x = 0;	//sin()
	m_vMoistureDropsOffsets.y -= 2 * fDropsSpeed;	//move down
	m_vMoistureDropsOffsets.z = 0;	//sin()
	m_vMoistureDropsOffsets.w -= fDropsSpeed;

	//update factors
	m_vMoistureDropsFactors.x += (m_fMoistureFade - m_vMoistureDropsFactors.x) * 2.5f * fMoistureDelta;
	m_vMoistureDropsFactors.y += (m_fMoistureFade - m_vMoistureDropsFactors.x) * 4.0f * fMoistureDelta;
	m_vMoistureDropsFactors.z += (m_fMoistureFade - m_vMoistureDropsFactors.x) * 5.0f * fMoistureDelta;	
	m_vMoistureDropsFactors.w = 0;
}



Matrix44 CRainDrops::ComputeCurrentView( int iViewportWidth, int iViewportHeight )
{
	Matrix44 pCurrView( PostProcessUtils().m_pView );
	Matrix44 pCurrProj( PostProcessUtils().m_pProj );	

	Matrix33 pLerpedView;
	pLerpedView.SetIdentity(); 

	float fCurrFrameTime = gEnv->pTimer->GetFrameTime();
	// scale down speed a bit
	float fAlpha = iszero (fCurrFrameTime) ? 0.0f : .0005f/( fCurrFrameTime);            

	// Interpolate matrixes and position
	pLerpedView = Matrix33(pCurrView)*(1-fAlpha) + Matrix33(m_pPrevView)*fAlpha;   

	Vec3 pLerpedPos = Vec3::CreateLerp(pCurrView.GetRow(3), m_pPrevView.GetRow(3), fAlpha);     

	// Compose final 'previous' viewProjection matrix
	Matrix44 pLView = pLerpedView;
	pLView.m30 = pLerpedPos.x;   
	pLView.m31 = pLerpedPos.y;
	pLView.m32 = pLerpedPos.z; 

	m_pViewProjPrev = pLView * pCurrProj;      
	m_pViewProjPrev.Transpose();

	// Compute camera velocity vector
	Vec3 vz = gcpRendD3D->GetRCamera().Z;    // front vec
	Vec3 vMoveDir = gcpRendD3D->GetRCamera().Orig - vz;
	Vec4 vCurrPos = Vec4(vMoveDir.x, vMoveDir.y, vMoveDir.z, 1.0f);

	Matrix44 pViewProjCurr ( PostProcessUtils().m_pViewProj );

	Vec4 pProjCurrPos = pViewProjCurr * vCurrPos;

	pProjCurrPos.x = ( (pProjCurrPos.x + pProjCurrPos.w) * 0.5f + (1.0f / (float) iViewportWidth) * pProjCurrPos.w) / pProjCurrPos.w;
	pProjCurrPos.y = ( (pProjCurrPos.w - pProjCurrPos.y) * 0.5f + (1.0f / (float) iViewportHeight) * pProjCurrPos.w) / pProjCurrPos.w;

	Vec4 pProjPrevPos = m_pViewProjPrev * vCurrPos;

	pProjPrevPos.x = ( (pProjPrevPos.x + pProjPrevPos.w) * 0.5f + (1.0f / (float) iViewportWidth) * pProjPrevPos.w) / pProjPrevPos.w;
	pProjPrevPos.y = ( (pProjPrevPos.w - pProjPrevPos.y) * 0.5f + (1.0f / (float) iViewportHeight) * pProjPrevPos.w) / pProjPrevPos.w;

	m_pVelocityProj = Vec3(pProjCurrPos.x - pProjPrevPos.x, pProjCurrPos.y - pProjPrevPos.y, 0);

	return pCurrView;
}

void CRainDrops::Render()
{
  PROFILE_LABEL_PUSH("RAIN_DROPS");

  gRenDev->m_cEF.mfRefreshSystemShader("PostEffectsGame", CShaderMan::m_shPostEffectsGame);

  int iViewportX, iViewportY, iViewportWidth, iViewportHeight;
  gcpRendD3D->GetViewport(&iViewportX, &iViewportY, &iViewportWidth, &iViewportHeight);

  Matrix44 pCurrView = ComputeCurrentView(iViewportWidth, iViewportHeight);

  uint16	uPrevDytex	= (m_uCurrentDytex + 1) % 2;
  CTexture*&	rpPrevDytex = CTexture::s_ptexRainDropsRT[uPrevDytex];
  assert(rpPrevDytex);  
  CTexture*&	rpCurrDytex = CTexture::s_ptexRainDropsRT[m_uCurrentDytex];
  assert(rpCurrDytex);  
  int		iRTWidth	= rpCurrDytex->GetWidth();
  int		iRTHeight	= rpCurrDytex->GetHeight();

  UpdateParticles(iRTWidth, iRTHeight);
  UpdateMoisture();	
  

  gcpRendD3D->FX_PushRenderTarget(0, rpCurrDytex, NULL);
  gcpRendD3D->RT_SetViewport(0, 0, iRTWidth, iRTHeight);
  {
	  ApplyExtinction(rpPrevDytex, iViewportWidth, iViewportHeight, iRTWidth, iRTHeight);
	  DrawMoisture(iRTWidth, iRTHeight);
	  DrawRaindrops(iViewportWidth, iViewportHeight, iRTWidth, iRTHeight);
  }
  
  gcpRendD3D->FX_PopRenderTarget(0);

	gcpRendD3D->RT_SetViewport(iViewportX, iViewportY, iViewportWidth, iViewportHeight);

  DrawFinal(rpCurrDytex);

  m_uCurrentDytex = (m_uCurrentDytex + 1) % 2;  

  // store previous frame data
  m_pPrevView =  pCurrView;
  m_bFirstFrame = false;

  PROFILE_LABEL_POP("RAIN_DROPS");
}


void CRainDrops::DrawMoisture(int iRTWidth, int iRTHeight)
{
	if(m_fMoistureCoverage > EPSILON)
	{
		PROFILE_LABEL_PUSH("RAIN_DROPS_MOISTURE");

		//clear and set render flags
		uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;  
		gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];	//_RT_SAMPLE0

		static CCryNameTSCRC pTech0Name("MoistureGen");
		PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffectsGame, pTech0Name, FEF_DONTSETTEXTURES| FEF_DONTSETSTATES);

		gcpRendD3D->FX_SetState(GS_BLSRC_ONE | GS_BLDST_ONE | GS_NODEPTHTEST); //additive

		// override blend operation from additive to max (max(src,dst) gets written)
#if defined (DIRECT3D9)
		gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX);
#elif defined (DIRECT3D10)
		SStateBlend bl = gcpRendD3D->m_StatesBL[gcpRendD3D->m_nCurStateBL];
		bl.Desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_MAX;
		bl.Desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_MAX;
		gcpRendD3D->SetBlendState(&bl);
#endif

		static CCryNameR pParam0Name("vRainNormalMapParams");
		static const Vec4 vParam0(1.0f, 1.0f, 1.0f, -1.0f);
		CShaderMan::m_shPostEffectsGame->FXSetVSFloat(pParam0Name, &vParam0, 1);	//PostProcessUtils().ShSetParamVS(pParam0Name, Vec4(1.0f, 1.0f, 1.0f, -1.0f));

		static CCryNameR pParam1Name("vRainParams");
		static const Vec4 vParam1(1.0f, 1.0f, 1.0f, 1.0f);
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam1Name, &vParam1, 1);	//PostProcessUtils().ShSetParamPS(pParam1Name, Vec4(1.0f, 1.0f, 1.0f, 1.0f));

		static CCryNameR pParam2Name("vMoistureParams");
		Vec4 vMoistureParams(
			m_fMoistureCoverage,
			m_pMoistureHardness->GetParam(),
			m_fMoistureStrength,
			m_fMoistureFogCoverage
			);		
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam2Name, &vMoistureParams, 1);				//PostProcessUtils().ShSetParamPS(pParam2Name, vMoistureParams);

		static CCryNameR pParam3Name("vMoistureNoiseOffsets");		
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam3Name, &m_vMoistureNoiseOffsets, 1);		//PostProcessUtils().ShSetParamPS(pParam3Name, m_vMoistureNoiseOffsets);

		static CCryNameR pParam4Name("vMoistureDropsOffsets");		
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam4Name, &m_vMoistureDropsOffsets, 1);		//PostProcessUtils().ShSetParamPS(pParam4Name, m_vMoistureDropsOffsets);

		static CCryNameR pParam5Name("vMoistureRandom");
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam5Name, &m_vMoistureRandom, 1);				//PostProcessUtils().ShSetParamPS(pParam5Name, m_vMoistureRandom);

		static CCryNameR pParam6Name("vMoistureDropsFactors");
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam6Name, &m_vMoistureDropsFactors, 1);		//PostProcessUtils().ShSetParamPS(pParam6Name, m_vMoistureDropsFactors);


		PostProcessUtils().SetTexture(m_ptexMoisture, 0, FILTER_LINEAR, TADDR_MIRROR);
		PostProcessUtils().SetTexture(m_ptexNoise, 1, FILTER_LINEAR, TADDR_MIRROR);
		PostProcessUtils().DrawFullScreenQuad(iRTWidth, iRTHeight);

		// restore previous blend operation
#if defined (DIRECT3D9)
		gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
#elif defined (DIRECT3D10)
		bl.Desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
		bl.Desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
		gcpRendD3D->SetBlendState(&bl);
#endif

		PostProcessUtils().ShEndPass();

		//reset render flags  
		gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
		PROFILE_LABEL_POP("RAIN_DROPS_MOISTURE");
	}
}


void CRainDrops::DrawRaindrops( int iViewportWidth, int iViewportHeight, int iRTWidth, int iRTHeight )
{
	PROFILE_LABEL_PUSH("RAIN_DROPS_RAINDROPS");

	float fScreenW = iRTWidth;
	float fScreenH = iRTHeight;

	float fInvScreenW = 1.0f / fScreenW;
	float fInvScreenH = 1.0f / fScreenH;


	gcpRendD3D->Set2DMode(false, 1, 1);       
	gcpRendD3D->Set2DMode(true, iRTWidth, iRTHeight);

	//clear and set render flags
	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;  
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

	//if( fDot >= - 0.25)
	{

		// render particles into effects rain map
		static CCryNameTSCRC pTech0Name("RainDropsGen");

		PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffectsGame, pTech0Name, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);   

		gcpRendD3D->FX_SetState(GS_BLSRC_ONE | GS_BLDST_ONE |  GS_NODEPTHTEST); //additive

		// override blend operation from additive to max (max(src,dst) gets written)
#if defined (DIRECT3D9)
		gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_MAX);
#elif defined (DIRECT3D10)
		SStateBlend bl = gcpRendD3D->m_StatesBL[gcpRendD3D->m_nCurStateBL];
		bl.Desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_MAX;
		bl.Desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_MAX;
		gcpRendD3D->SetBlendState(&bl);
#endif

		SRainDropsItor pItor, pItorEnd = m_pDropsLst.end(); 
		for(pItor=m_pDropsLst.begin(); pItor!=pItorEnd; ++pItor )
		{
			SRainDrop *pCurr = (*pItor);

			if( pCurr->m_fSize < 0.01f) 
			{
				continue;
			}

			// render a sprite    
			float x0 = pCurr->m_pPos.x*fScreenW;
			float y0 = pCurr->m_pPos.y*fScreenH;  

			float x1 = (pCurr->m_pPos.x + pCurr->m_fSize * (fScreenH/fScreenW)) * fScreenW ;
			float y1 = (pCurr->m_pPos.y + pCurr->m_fSize) * fScreenH ;          

			float fCurrLifeTime = (PostProcessUtils().m_pTimer->GetCurrTime() - pCurr->m_fSpawnTime) / pCurr->m_fLifeTime;
			Vec4 vRainParams = Vec4(1.0f, 1.0f, 1.0f, 1.0f - fCurrLifeTime);

			static CCryNameR pParam0Name("vRainParams");
			CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pParam0Name, &vRainParams, 1);			
			PostProcessUtils().DrawScreenQuad(256, 256, x0, y0, x1, y1);
		}

		// restore previous blend operation
#if defined (DIRECT3D9)
		gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
#elif defined (DIRECT3D10)
		bl.Desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
		bl.Desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
		gcpRendD3D->SetBlendState(&bl);
#endif

		PostProcessUtils().ShEndPass();   
	}

	gcpRendD3D->Set2DMode(false, 1, 1);   
	gcpRendD3D->Set2DMode(true, 1, 1); 

	gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
	PROFILE_LABEL_POP("RAIN_DROPS_RAINDROPS");
}

void CRainDrops::ApplyExtinction( CTexture*& rptexPrevRT, int iViewportWidth, int iViewportHeight, int iRTWidth, int iRTHeight )
{
	//restore black texture to backbuffer for cleaning
	PostProcessUtils().CopyTextureToScreen(CTexture::s_ptexBlack);

	PROFILE_LABEL_PUSH("RAIN_DROPS_EXTINCTION");
	if(!m_bFirstFrame)
	{
		// Store camera parameters
		Vec3 vx = gcpRendD3D->GetRCamera().X;    // up vec
		Vec3 vy = gcpRendD3D->GetRCamera().Y;    // right vec
		Vec3 vz = gcpRendD3D->GetRCamera().Z;    // front vec
		float fDot = vz.Dot(Vec3(0, 0, -1.0f));
		float fGravity = (1.0f - fabs( fDot ));  
		float fFrameScale = 4.0f * gEnv->pTimer->GetFrameTime();
		Vec4 vRainNormalMapParams = Vec4(m_pVelocityProj.x * ((float) iViewportWidth), 0, fFrameScale * fGravity, fFrameScale*1.0f + m_pVelocityProj.y * ((float) iViewportHeight)) * 0.25f;

		//texture to restore
		CTexture*	pPrevTexture = rptexPrevRT;

		// apply extinction
		static CCryNameTSCRC pTech1Name("RainDropsExtinction");
		PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffectsGame, pTech1Name, FEF_DONTSETSTATES);   //FEF_DONTSETTEXTURES | 

		gcpRendD3D->FX_SetState(GS_NODEPTHTEST);    

		static CCryNameR pParam1Name("vRainNormalMapParams");
		PostProcessUtils().ShSetParamVS(pParam1Name, vRainNormalMapParams);

		vRainNormalMapParams.w = fFrameScale;
		static CCryNameR pParam0Name("vRainParams");
		PostProcessUtils().ShSetParamPS(pParam0Name, vRainNormalMapParams);

		PostProcessUtils().SetTexture(pPrevTexture, 0, FILTER_LINEAR);
		PostProcessUtils().SetTexture(m_ptexNoise, 1, FILTER_LINEAR, TADDR_MIRROR);

		PostProcessUtils().DrawFullScreenQuad(iRTWidth, iRTHeight);

		PostProcessUtils().ShEndPass();
	}
	PROFILE_LABEL_POP("RAIN_DROPS_EXTINCTION");
}


void CRainDrops::DrawFinal(CTexture*& rptexCurrRT)
{
	PROFILE_LABEL_PUSH("RAIN_DROPS_FINAL");

	//clear and set render flags
	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;  
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

	//if(m_bMoisture)
	if(m_fMoistureCoverage > EPSILON)
	{
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];	//_RT_SAMPLE0

		if(m_fMoistureFogCoverage > EPSILON)
			gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];	//_RT_SAMPLE1
	}

	// display rain
	static CCryNameTSCRC pTechName("RainDropsFinal");
	PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffectsGame, pTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

	gcpRendD3D->FX_SetState(GS_NODEPTHTEST);    

	static CCryNameR pParam0Name("vRainNormalMapParams");
	PostProcessUtils().ShSetParamVS(pParam0Name, Vec4(1.0f, 1.0f, 1.0f, -1.0f));
	static CCryNameR pParam1Name("vRainParams");
	PostProcessUtils().ShSetParamPS(pParam1Name, Vec4(1.0f, 1.0f, 1.0f, 1.0f));

	//if(m_bMoisture)
	if(m_fMoistureCoverage > EPSILON)
	{
		static CCryNameR pParam2Name("vMoistureParams");
		Vec4 vMoistureParams(
			m_fMoistureCoverage,
			m_pMoistureHardness->GetParam(),
			m_fMoistureStrength,
			m_fMoistureFogCoverage
			);
		PostProcessUtils().ShSetParamPS(pParam2Name, vMoistureParams);

		static CCryNameR pParam3Name("vMoistureNoiseOffsets");   
		PostProcessUtils().ShSetParamPS(pParam3Name, m_vMoistureNoiseOffsets);

		static CCryNameR pParam4Name("vMoistureRandom");
		PostProcessUtils().ShSetParamPS(pParam4Name, m_vMoistureRandom);
	}

	PostProcessUtils().SetTexture(CTexture::s_ptexBackBuffer, 0, FILTER_LINEAR);   
	PostProcessUtils().SetTexture(rptexCurrRT, 1, FILTER_LINEAR, TADDR_MIRROR);
	PostProcessUtils().SetTexture(m_ptexNoise, 2, FILTER_LINEAR, TADDR_MIRROR);

	PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());

	PostProcessUtils().ShEndPass();

	//reset render flags  
	gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
	PROFILE_LABEL_POP("RAIN_DROPS_FINAL");
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CSceneRain::CreateBuffers(uint16 nVerts, void *&pINVB, SVF_P3F_C4B_T2F *pVtxList)
{
  D3DVertexBuffer *pVB = 0;

  HRESULT hr = S_OK;
  D3DDevice *dv = gcpRendD3D->GetDevice();

  // Create VB/IB
#if defined (DIRECT3D9)
  hr = dv->CreateVertexBuffer( nVerts * sizeof( SVF_P3F_C4B_T2F ), D3DUSAGE_WRITEONLY,
    0, D3DPOOL_DEFAULT, &pVB, NULL );
  assert(SUCCEEDED(hr));
  if(FAILED(hr))
    return;

  SVF_P3F_C4B_T2F* pVerts = NULL;
  uint16* pInds = NULL;

  //allocate vertices
  hr = pVB->Lock(0, nVerts * sizeof( SVF_P3F_C4B_T2F ), (void **) &pVerts, 0);
  assert(SUCCEEDED(hr));

  memcpy( pVerts, pVtxList, nVerts * sizeof( SVF_P3F_C4B_T2F ) );

  hr = pVB->Unlock();
  assert(SUCCEEDED(hr));

#elif defined (DIRECT3D10)
  D3D11_BUFFER_DESC BufDesc;
  ZeroStruct(BufDesc);
  BufDesc.ByteWidth = nVerts * sizeof( SVF_P3F_C4B_T2F );
  BufDesc.Usage = D3D11_USAGE_IMMUTABLE;
  BufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  BufDesc.CPUAccessFlags = 0;
  BufDesc.MiscFlags = 0;

  D3D11_SUBRESOURCE_DATA SubResData;
  ZeroStruct(SubResData);
  SubResData.pSysMem = pVtxList;
  SubResData.SysMemPitch = 0;
  SubResData.SysMemSlicePitch = 0;

  hr = dv->CreateBuffer(&BufDesc, &SubResData, (ID3D11Buffer **)&pVB);
  assert(SUCCEEDED(hr));
#endif

  pINVB = pVB;
}

int CSceneRain::CreateResources()
{
  Release();

	static const int nSlices = 12;
	const float nSliceStep( DEG2RAD( 360.0f / (float) nSlices ) );

	std::vector<SVF_P3F_C4B_T2F> pVB;
  SVF_P3F_C4B_T2F vVertex;
	vVertex.color.dcolor = ~0;
	vVertex.st = Vec2(0.0f, 0.0f);

  // Generate cylinder vertices
  for (int h = 0; h < nSlices + 1; ++h)
  {
    vVertex.xyz = Vec3( cosf( ((float)h) * nSliceStep ),
                        sinf( ((float)h) * nSliceStep ), 
                        0.0f );
    pVB.push_back(vVertex);
    vVertex.xyz.z = 1.f;
    pVB.push_back(vVertex);
  }
	// Base of cylinder to cover also lower parts of screen instead of custom RT clearing
	vVertex.xyz.z = 0.f;
	pVB.push_back(vVertex);
	pVB.push_back(vVertex);
	int nL = 0;
	int nH = nSlices * 2;
	while (nL < nH)
	{
		nL += 2;
		nH -= 2;
		pVB.push_back(pVB[nL]);
		pVB.push_back(pVB[nH]);
	}
	pVB.push_back(pVB[nL]);

  m_nConeVBSize = pVB.size();
  CreateBuffers(m_nConeVBSize, m_pConeVB, &pVB[0]);

  return 1;
}

void CSceneRain::Release()
{
  D3DVertexBuffer *pVB = (D3DVertexBuffer*)m_pConeVB;
  SAFE_RELEASE(pVB);
	m_pConeVB = 0;
	if (CTexture::IsTextureExist(CTexture::s_ptexRainDrops))
		CTexture::s_ptexRainDrops->hintRelease();
	m_bReinit = true;
}


void CSceneRain::Render()
{
	if (!m_pConeVB)
	{
		CreateResources();
	}

	PROFILE_LABEL_PUSH("RAIN");

  gRenDev->m_cEF.mfRefreshSystemShader("PostEffectsGame", CShaderMan::m_shPostEffectsGame);

	int rainBufferWidth = gcpRendD3D->GetWidth();
	int rainBufferHeight = gcpRendD3D->GetHeight();
	if (gRenDev->m_RP.m_eQuality < eRQ_High)
	{
		rainBufferWidth >>= 1;
		rainBufferHeight >>= 1;
	}

	const bool bInit = !CTexture::IsTextureExist(CTexture::s_ptexRainDrops)
		|| CTexture::s_ptexRainDrops->GetWidth() != rainBufferWidth || CTexture::s_ptexRainDrops->GetHeight() != rainBufferHeight;
	if (bInit)
	{
		if (!CTexture::s_ptexRainDrops->Create2DTexture(rainBufferWidth, rainBufferHeight, 1,
			FT_DONT_RELEASE | FT_DONT_STREAM | FT_DONT_RESIZE | FT_USAGE_RENDERTARGET,
			0, eTF_A8R8G8B8, eTF_A8R8G8B8))
			return;
	}
	CTexture::s_ptexRainDrops->SetRenderTargetTile(1);

  D3DVertexBuffer *pVB = (D3DVertexBuffer*)m_pConeVB;
	uint16 nVerts = m_nConeVBSize;

  gcpRendD3D->Set2DMode(false, 1, 1);    
	gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexRainDrops, NULL);
	gcpRendD3D->RT_SetViewport(0, 0, rainBufferWidth, rainBufferHeight);        

	const bool bMultiGPU = gcpRendD3D->IsMultiGPUModeActive();
	const float fAmount = m_RainVolParams.m_fAmount * CRenderer::CV_r_rainamount;
	const int nLayers = clamp_tpl(int(fAmount * 4), 2, 4);
	static int nPrevLayers = nLayers;
	const int nLayersPerFrame = bMultiGPU ? nLayers : clamp_tpl(CRenderer::CV_r_rainLayersPerFrame, 1, nLayers);
	const int nOtherLayerID = (gcpRendD3D->GetFrameID() % max(nLayers - nLayersPerFrame + 1, 1)) + 1;
	const int nPasses = max(nLayersPerFrame, 1);

	if (bInit
		|| m_bReinit
		|| bMultiGPU
		|| nLayers < nPrevLayers
		|| nPasses < 2
		|| nPasses == 4)
	{
		ColorF clearColor(0, 0, 0, 0);
		gcpRendD3D->EF_ClearBuffers(FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE, &clearColor);
		m_bReinit = false;
	}









	nPrevLayers = nLayers;
	
	CCryNameTSCRC pTechName("SceneRain");
  static CCryNameR pParamName("sceneRainParams");

	// Get lightning color - will just use for overbright rain
	Vec3 v;
	gEnv->p3DEngine->GetGlobalParameter(E3DPARAM_SKY_HIGHLIGHT_COLOR, v);
	const float fHighlight = 2.f * v.len();
	Vec4 pParams = Vec4(0.f, 0.f, 0.f, 0.f);
	const float fSizeMult = max(CRenderer::CV_r_rainDistMultiplier, 1e-3f) * max(0.2f * m_RainVolParams.m_fUmbrellaRadius, 1.f) * 0.5f;

	if (m_RainVolParams.m_bApplyOcclusion)
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
	else
		gRenDev->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0];

  for (int pass = 0; pass < nPasses; ++pass)
	{
		int nCurLayer = pass;
		if (nLayersPerFrame < nLayers)
			nCurLayer = pass < max(nLayersPerFrame - 1, 1) ? pass : nOtherLayerID;

		int nState = GS_NODEPTHTEST;
		switch (nCurLayer)
		{
		default:
		case 0:
			nState |= GS_NOCOLMASK_G | GS_NOCOLMASK_B | GS_NOCOLMASK_A;
			break;
		case 1:
			nState |= GS_NOCOLMASK_R | GS_NOCOLMASK_B | GS_NOCOLMASK_A;
			break;
		case 2:
			nState |= GS_NOCOLMASK_R | GS_NOCOLMASK_G | GS_NOCOLMASK_A;
			break;
		case 3:
			nState |= GS_COLMASK_A;
			break;
		}

		PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffectsGame, pTechName, FEF_DONTSETSTATES);   

		gcpRendD3D->FX_SetState(nState);
		gcpRendD3D->D3DSetCull(eCULL_None);

		pParams.x = m_RainVolParams.m_fRainDropsSpeed;
		pParams.z = float(nCurLayer) / max(nLayers - 1, 1);
		pParams.y = fHighlight * (1.f - pParams.z);
		pParams.w = (nCurLayer+1) * fSizeMult;
		pParams.w = powf(pParams.w, 1.5f);
		CShaderMan::m_shPostEffects->FXSetVSFloat(pParamName, &pParams, 1);
		pParams.x = fAmount;



		pParams.w = 1.f - m_RainVolParams.m_fUmbrellaRadius / pParams.w;

		CShaderMan::m_shPostEffects->FXSetPSFloat(pParamName, &pParams, 1);

		if (m_RainVolParams.m_bApplyOcclusion)
		{
			static CCryNameR param3Name("rainOcc_TransMat");
			CShaderMan::m_shPostEffects->FXSetVSFloat(param3Name, (Vec4 *)m_RainVolParams.m_matOccTransRender.GetData(), 3);
		}

		gcpRendD3D->FX_SetVStream( 0, pVB, 0, sizeof( SVF_P3F_C4B_T2F ) );

		if (!FAILED(gcpRendD3D->FX_SetVertexDeclaration( 0, eVF_P3F_C4B_T2F )))
		{
			gcpRendD3D->FX_Commit();
			gcpRendD3D->FX_DrawPrimitive(eptTriangleStrip, 0, nVerts);
		}

		PostProcessUtils().ShEndPass();
	}

	gcpRendD3D->FX_PopRenderTarget(0);
  gcpRendD3D->Set2DMode(true, 1, 1);

	gRenDev->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_SAMPLE0];

	// HACK (re-set back-buffer): due to lazy RT updates/setting there's strong possibility we run into problems on x360 when we try to resolve from edram with no RT set
	gcpRendD3D->FX_SetActiveRenderTargets();

	PROFILE_LABEL_POP("RAIN");
}


bool CSceneRain::Preprocess()
{
	bool bIsActive = IsActive()
		&& !(m_RainVolParams.m_bApplyOcclusion && m_RainVolParams.m_AreaAABB.IsReset())
		&& m_RainVolParams.m_bRainDrops;
	if (!bIsActive)
		Release();

	return bIsActive;
}


void CSceneRain::Reset(bool bOnSpecChange)
{
	if (CTexture::s_ptexRainDrops)
		CTexture::s_ptexRainDrops->hintRelease();
	m_pActive->ResetParam(0);
	m_RainVolParams.m_AreaAABB.Reset();
}

void CSceneRain::OnLostDevice()
{
	Release();
}

const char *CSceneRain::GetName() const
{
	return "SceneRain";
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

const char *CSceneSnow::GetName() const
{
	return "SceneSnow";
}

bool CSceneSnow::Preprocess()
{  
	//bool bSnowActive = IsActiveSnow();

	// HDR now.
	return false;//bSnowActive;
}

int CSceneSnow::CreateResources()
{
	Release();
	
	// Already generated ? No need to proceed
	if( !m_pClusterList.empty() )
	{
		return 1;
	}

	m_nNumClusters = max(1, CRenderer::CV_r_snowFlakeClusters);
	m_pClusterList.reserve( m_nNumClusters );
	for(int p=0; p< m_nNumClusters; p++)
	{
		SSnowCluster *pDrop = new SSnowCluster;
		m_pClusterList.push_back( pDrop );
	}
	
	return 1;
}

void CSceneSnow::Release()
{
	if(m_pClusterList.empty())
	{
		return;
	}

	SSnowClusterItor pItor, pItorEnd = m_pClusterList.end(); 
	for(pItor=m_pClusterList.begin(); pItor!=pItorEnd; ++pItor )
	{
		SAFE_DELETE((*pItor));
	} 
	m_pClusterList.clear(); 

	SAFE_RELEASE( m_pSnowFlakeMesh );
}


void CSceneSnow::Reset(bool bOnSpecChange)
{
	m_pActive->ResetParam(0.0f);  
	m_nAliveClusters = 0;

	SAFE_RELEASE( m_pSnowFlakeMesh );
}


bool CSceneSnow::IsActiveSnow()
{
	//static float s_fLastSpawnTime = -1.0f;

	if( (m_pActive->GetParam() > 0.09f && CRenderer::CV_r_snow && m_SnowVolParams.m_fSnowFallBrightness > 0.005f && m_SnowVolParams.m_nSnowFlakeCount > 0) || m_nAliveClusters)
	{
		//s_fLastSpawnTime = 0.0f;
		return true;
	}

	/*if( s_fLastSpawnTime == 0.0f)
	{
		s_fLastSpawnTime = PostProcessUtils().m_pTimer->GetCurrTime();
	}

	if( fabs(PostProcessUtils().m_pTimer->GetCurrTime() - s_fLastSpawnTime) < 1.0f ) 
	{
		return true;
	}*/

	return false;
}

bool CSceneSnow::GenerateClusterMesh()
{
	int	iRTWidth  = gcpRendD3D->GetWidth();
	int	iRTHeight = gcpRendD3D->GetHeight();
	float fAspect = float(iRTWidth)/iRTHeight;

	// Vertex offsets for sprite expansion.
	static const Vec2 vVertOffsets[6] =
	{
		Vec2( 1,  1),
		Vec2(-1,  1),
		Vec2( 1, -1),
		Vec2( 1, -1),
		Vec2(-1,  1),
		Vec2(-1, -1)
	};

	// Create mesh if there isn't one or if the count has changed.
	if( !m_pSnowFlakeMesh || (m_nFlakesPerCluster != m_SnowVolParams.m_nSnowFlakeCount))
	{
		SAFE_RELEASE(m_pSnowFlakeMesh);

		m_nFlakesPerCluster = m_SnowVolParams.m_nSnowFlakeCount;
		m_nSnowFlakeVertCount = m_nFlakesPerCluster * 6;

		SVF_P3F_T2F_T3F* pSnowFlakes = new SVF_P3F_T2F_T3F[m_nSnowFlakeVertCount];

		float fUserSize = 0.0075f;
		float fUserSizeVar = 0.0025f;

		// Loop through number of flake sprites (6 verts per sprite).
		for(int p=0; p < m_nSnowFlakeVertCount; p+=6)
		{
			Vec3 vPosition = Vec3((Random()*2-1)*10, (Random()*2-1)*10, (Random()*2-1)*10);
			float fSize = fUserSize + fUserSizeVar * (Random()*2.0f-1.0f);
			float fRandPhase = Random() * 10.0f;

			// Triangle strip, each vertex gets it's own offset for sprite expansion.
			for(int i=0; i < 6; ++i)
			{
				int nIdx = p+i;
				pSnowFlakes[nIdx].p = vPosition;
				pSnowFlakes[nIdx].st0 = Vec2(vVertOffsets[i].x, vVertOffsets[i].y);
				pSnowFlakes[nIdx].st1 = Vec3(fSize, fSize * fAspect, fRandPhase);
			}
		}

		m_pSnowFlakeMesh = gRenDev->CreateRenderMeshInitialized(pSnowFlakes, m_nSnowFlakeVertCount, eVF_P3F_T2F_T3F, 0, 0, prtTriangleList, "SnowFlakeBuffer", "SnowFlakeBuffer");

		delete [] pSnowFlakes;

		// If the mesh wasn't initialized properly, return false.
		if(!m_pSnowFlakeMesh)
			return false;
	}

	return true;
}

void CSceneSnow::SpawnCluster( SSnowCluster *&pCluster )
{
	static SSnowCluster pNewDrop;

	static float fLastSpawnTime = 0.0f;

	pCluster->m_pPos = gcpRendD3D->GetRCamera().Orig;

	pCluster->m_pPos.x += (Random()*2-1)*15;
	pCluster->m_pPos.y += (Random()*2-1)*15;
	pCluster->m_pPos.z += (Random()*2-1)*5 + 4;

	pCluster->m_pPosPrev = pCluster->m_pPos;		
	pCluster->m_fLifeTime = (pNewDrop.m_fLifeTime/max(1.0f, m_SnowVolParams.m_fSnowFallGravityScale) + pNewDrop.m_fLifeTimeVar *(Random()*2.0f-1.0f) );

	// Increase/decrease weight randomly 
	pCluster->m_fWeight = clamp_tpl<float>(pNewDrop.m_fWeight + pNewDrop.m_fWeightVar * (Random()*2.0f-1.0f), 0.1f, 1.0f );  
		
 	pCluster->m_fSpawnTime = PostProcessUtils().m_pTimer->GetCurrTime();
 	fLastSpawnTime = PostProcessUtils().m_pTimer->GetCurrTime();
}

void CSceneSnow::UpdateClusters()
{
	SSnowClusterItor pItor, pItorEnd = m_pClusterList.end();

	bool bAllowSpawn( m_pActive->GetParam() > 0.005f && m_SnowVolParams.m_fSnowFallBrightness > 0.005f );

	const float fCurrFrameTime = gEnv->pTimer->GetFrameTime();
	const float fGravity = fCurrFrameTime * m_SnowVolParams.m_fSnowFallGravityScale;
	const float fWind = m_SnowVolParams.m_fSnowFallWindScale;

	m_nAliveClusters = 0;

	Vec3 vCameraPos = gcpRendD3D->GetRCamera().Orig;

	if (!PostProcessUtils().m_pTimer)
	{
		return;
	}

	for(pItor=m_pClusterList.begin(); pItor!=pItorEnd; ++pItor )
	{
		SSnowCluster *pCurr = (*pItor);

		float fCurrLifeTime = (PostProcessUtils().m_pTimer->GetCurrTime() - pCurr->m_fSpawnTime) / pCurr->m_fLifeTime;
		float fClusterDist = pCurr->m_pPos.GetDistance(vCameraPos);

		// Cluster is too small, died or is out of range, respawn.
		if( fabs(fCurrLifeTime) > 1.0f || pCurr->m_fLifeTime < 0 || fClusterDist > 30.0f)
		{
			if( bAllowSpawn )
			{
				SpawnCluster( pCurr );
			}
			else
			{
				pCurr->m_fLifeTime = -1.0f;
				continue;
			}
		}

		// Previous position (for blur).
		pCurr->m_pPosPrev = pCurr->m_pPos;

		// Apply gravity force.
		if(m_SnowVolParams.m_fSnowFallGravityScale)
		{
			Vec3 vGravity(0.0f,0.0f,-9.8f);
			if(gEnv->pPhysicalWorld)
			{
				pe_params_buoyancy pb;
				gEnv->pPhysicalWorld->CheckAreas(pCurr->m_pPos, vGravity, &pb);
			}
			pCurr->m_pPos += vGravity * fGravity * pCurr->m_fWeight;
		}

		// Apply wind force.
		if(m_SnowVolParams.m_fSnowFallWindScale)
		{
			Vec3 vWindVec = gEnv->p3DEngine->GetWind( AABB(pCurr->m_pPos - Vec3(10,10,10), pCurr->m_pPos + Vec3(10,10,10)), false );
			pCurr->m_pPos += vWindVec * pCurr->m_fWeight * fWind;
		}

		// Increment count.
		m_nAliveClusters++;
	}
}

void CSceneSnow::Render()
{
	// Number of clusters has changed, reallocate resources.
	const int nNumClusters = max(1, CRenderer::CV_r_snowFlakeClusters);
	if(m_nNumClusters != nNumClusters)
		CreateResources();

	// Generate the cluster mesh.
	if(!GenerateClusterMesh())
		return;

	// If not enough flakes, skip.
	if(m_nFlakesPerCluster < 1)
		return;

	gRenDev->m_cEF.mfRefreshSystemShader("PostEffectsGame", CShaderMan::m_shPostEffectsGame);

	PostProcessUtils().StretchRect(CTexture::s_ptexHDRTarget, CTexture::s_ptexSceneTarget);

	UpdateClusters();
	
	CTexture *pSceneSrc = CTexture::s_ptexHDRTarget;
	CTexture *pVelocitySrc = CTexture::s_ptexBackBuffer;
	if(CRenderer::CV_r_snow_halfres)
	{
		pSceneSrc = CTexture::s_ptexHDRTargetScaledTmp[0];
		pVelocitySrc = CTexture::s_ptexBackBufferScaled[0];
	}

	// Clear the velocity buffer.
	if(CRenderer::CV_r_snow_halfres)
	{
		gcpRendD3D->FX_PushRenderTarget(0, pVelocitySrc, NULL);
		ColorF clearCol(127.0f/255.0f, 127.0f/255.0f, 0.0f, 0);		
		int32 clearFlags = FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE;
		gcpRendD3D->EF_ClearBuffers(clearFlags, &clearCol);
		gcpRendD3D->FX_PopRenderTarget(0);
	}
	
	PROFILE_LABEL_PUSH("SCENE_SNOW_FLAKES");

	gcpRendD3D->Set2DMode(false, 1, 1);

	// Render to HDR and velocity.
	gcpRendD3D->FX_PushRenderTarget(0, pSceneSrc, CRenderer::CV_r_snow_halfres ? NULL : PostProcessUtils().m_pCurDepthSurface);
	gcpRendD3D->FX_PushRenderTarget(1, pVelocitySrc, NULL);

	DrawClusters();

	gcpRendD3D->FX_PopRenderTarget(0);
	gcpRendD3D->FX_PopRenderTarget(1);

	gcpRendD3D->Set2DMode(true, 1, 1);

	PROFILE_LABEL_POP("SCENE_SNOW_FLAKES");

	if(CRenderer::CV_r_snow_halfres)
		HalfResComposite();

	gcpRendD3D->FX_SetActiveRenderTargets();
}

void CSceneSnow::HalfResComposite()
{
	PROFILE_LABEL_PUSH("SCENE_SNOW_FLAKES_HALFRES_COMPOSITE");
	gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexHDRTarget, NULL);
	gcpRendD3D->FX_PushRenderTarget(1, CTexture::s_ptexBackBuffer, NULL);

	static CCryNameTSCRC pTechNameComposite = "SnowHalfResComposite";
	SD3DPostEffectsUtils::ShBeginPass(CShaderMan::m_shPostEffectsGame, pTechNameComposite, FEF_DONTSETSTATES | FEF_DONTSETTEXTURES);
	gcpRendD3D->FX_SetState(GS_NODEPTHTEST | GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA);
	PostProcessUtils().SetTexture(CTexture::s_ptexHDRTargetScaledTmp[0], 0, FILTER_LINEAR);
	PostProcessUtils().SetTexture(CTexture::s_ptexBackBufferScaled[0], 1, FILTER_POINT);
	SD3DPostEffectsUtils::DrawFullScreenQuad(gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());
	SD3DPostEffectsUtils::ShEndPass();

	gcpRendD3D->FX_PopRenderTarget(0);
	gcpRendD3D->FX_PopRenderTarget(1);
	PROFILE_LABEL_POP("SCENE_SNOW_FLAKES_HALFRES_COMPOSITE");
}

void CSceneSnow::DrawClusters()
{	
	// Previous view projection matrix for motion blur.
	float fCurrFrameTime = gEnv->pTimer->GetFrameTime();
	Matrix44A mViewProjPrev = gRenDev->m_CameraMatrixPrev;
	mViewProjPrev = mViewProjPrev * GetUtils().m_pProj;
	mViewProjPrev.Transpose();

	//clear and set render flags
	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;  
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]);

	if (m_RainVolParams.m_bApplyOcclusion)
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];

	static CCryNameTSCRC pTech0Name("SceneSnow");

	static CCryNameR pSnowFlakeParamName("vSnowFlakeParams");
	Vec4 vSnowFlakeParams = Vec4(m_SnowVolParams.m_fSnowFallBrightness, min(10.0f, m_SnowVolParams.m_fSnowFlakeSize), m_SnowVolParams.m_fSnowFallTurbulence, m_SnowVolParams.m_fSnowFallTurbulenceFreq);

	SSnowClusterItor pItor, pItorEnd = m_pClusterList.end(); 
	for(pItor=m_pClusterList.begin(); pItor!=pItorEnd; ++pItor )
	{
		SSnowCluster *pCurr = (*pItor);
		if(pCurr->m_fLifeTime < 0)
			continue;
		
		// Don't render if indoors or under water.
		if(gEnv->p3DEngine->GetVisAreaFromPos(pCurr->m_pPos) || pCurr->m_pPos.z < gEnv->p3DEngine->GetWaterLevel(&pCurr->m_pPos))
			continue;

		PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffectsGame, pTech0Name, 0);

		// Snowflake params.
		CShaderMan::m_shPostEffectsGame->FXSetVSFloat(pSnowFlakeParamName, &vSnowFlakeParams, 1);
		CShaderMan::m_shPostEffectsGame->FXSetPSFloat(pSnowFlakeParamName, &vSnowFlakeParams, 1);

		// Cluster params.
		static CCryNameR vSnowClusterPosName("vSnowClusterPos");
		Vec4 vSnowClusterPos = Vec4(pCurr->m_pPos, 1.0f);
		CShaderMan::m_shPostEffectsGame->FXSetVSFloat(vSnowClusterPosName, &vSnowClusterPos, 1);

		static CCryNameR vSnowClusterPosPrevName("vSnowClusterPosPrev");
		Vec4 vSnowClusterPosPrev = Vec4(pCurr->m_pPosPrev, 1.0f);
		CShaderMan::m_shPostEffectsGame->FXSetVSFloat(vSnowClusterPosPrevName, &vSnowClusterPosPrev, 1);
			
		// Motion blur params.
		static CCryNameR pViewProjPrevName("mViewProjPrev");
		CShaderMan::m_shPostEffectsGame->FXSetVSFloat(pViewProjPrevName, (Vec4 *)mViewProjPrev.GetData(), 4);

		// Occlusion params.
		if (m_RainVolParams.m_bApplyOcclusion)
		{
			static CCryNameR mSnowOccMatrName("mSnowOccMatr");
			CShaderMan::m_shPostEffectsGame->FXSetVSFloat(mSnowOccMatrName, (Vec4 *)m_RainVolParams.m_matOccTransRender.GetData(), 3);
		}
		
		gcpRendD3D->FX_Commit();
		if (!FAILED(gcpRendD3D->FX_SetVertexDeclaration(0, eVF_P3F_T2F_T3F)))
		{
			int offset(0);
			CRenderMesh* pSnowFlakeMesh((CRenderMesh*) m_pSnowFlakeMesh);
			pSnowFlakeMesh->CheckUpdate(pSnowFlakeMesh->_GetVertexFormat(), 0);
			D3DVertexBuffer *pVB = gcpRendD3D->m_DevBufMan.GetD3DVB(pSnowFlakeMesh->_GetVBStream(VSF_GENERAL).m_nID, &offset);
			gcpRendD3D->FX_SetVStream(0, pVB, offset, pSnowFlakeMesh->GetStreamStride(VSF_GENERAL));
			gcpRendD3D->FX_SetIStream(0);

			gcpRendD3D->FX_DrawPrimitive(eptTriangleList, 0, m_nSnowFlakeVertCount);
		}

		PostProcessUtils().ShEndPass();   
	}
	gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}
